package com.nnnu.wsnackshop.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nnnu.wsnackshop.exception.ObjectException;
import com.nnnu.wsnackshop.mapper.ProductTagsMapper;
import com.nnnu.wsnackshop.mapper.TagsMapper;
import com.nnnu.wsnackshop.pojo.dto.AddTagDTO;
import com.nnnu.wsnackshop.pojo.dto.TagQueryDTO;
import com.nnnu.wsnackshop.pojo.dto.UpdateTagDTO;
import com.nnnu.wsnackshop.pojo.entity.ProductTags;
import com.nnnu.wsnackshop.pojo.entity.Tags;
import com.nnnu.wsnackshop.pojo.vo.TagExcelVO;
import com.nnnu.wsnackshop.pojo.vo.TagVO;
import com.nnnu.wsnackshop.result.PageResult;
import com.nnnu.wsnackshop.service.ITagsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品标签 服务实现类
 * </p>
 *
 * @author zk
 * @since 2025-05-14
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class TagsServiceImpl extends ServiceImpl<TagsMapper, Tags> implements ITagsService {

    private final ProductTagsMapper producttagsMapper;

    @Override
    public PageResult<TagVO> pageTags(@NotNull TagQueryDTO dto) {
        // 构建动态查询条件
        LambdaQueryWrapper<Tags> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StrUtil.isNotBlank(dto.getName()), Tags::getName, dto.getName())
                .eq(dto.getStatus() != null, Tags::getStatus, dto.getStatus()).orderByDesc(Tags::getUpdateAt); // 按更新时间倒序
        Page<Tags> page = baseMapper.selectPage(new Page<>(dto.getPageNum(), dto.getPageSize()), queryWrapper);
        List<TagVO> voList = page.getRecords().stream().map(this::toVO).collect(Collectors.toList());

        return new PageResult<>(page.getTotal(), voList);
    }

    @Override
    public void addTag(@NotNull AddTagDTO dto) {
        // 检查是否存在同名标签
        boolean exists = baseMapper.exists(new LambdaQueryWrapper<Tags>().eq(Tags::getName, dto.getName()));
        if (exists) {
            throw new ObjectException("标签名称已存在");
        }
        Tags tag = new Tags();
        tag.setName(dto.getName());
        tag.setStatus(1); // 默认启用
        baseMapper.insert(tag);
    }

    @Override
    public void updateTag(@NotNull UpdateTagDTO dto) {
        // 检查是否存在同名标签（排除自身）
        boolean exists = baseMapper
                .exists(new LambdaQueryWrapper<Tags>().eq(Tags::getName, dto.getName()).ne(Tags::getId, dto.getId()));
        if (exists) {
            throw new ObjectException("标签名称已存在");
        }
        Tags tag = new Tags();
        tag.setId(Math.toIntExact(dto.getId()));
        tag.setName(dto.getName());
        baseMapper.updateById(tag);
    }

    @Override
    public void updateStatus(Long id, Integer status) {
        Tags tag = new Tags();
        tag.setId(Math.toIntExact(id));
        tag.setStatus(status);
        baseMapper.updateById(tag);
    }

    @Override
    public TagVO getDetail(Long id) {

        Tags tag = baseMapper.selectById(id);
        if (tag == null) {
            throw new ObjectException("标签不存在: " + id);
        }
        return toVO(tag);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTags(List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new ObjectException("删除ID列表不能为空");
        }

        // 1. 校验所有ID对应的标签是否存在
        List<Tags> existTags = baseMapper.selectBatchIds(ids);
        if (existTags.size() != ids.size()) {
            // 找出不存在的ID
            Set<Integer> existIds = existTags.stream().map(Tags::getId).collect(Collectors.toSet());

            List<Integer> notExistIds = ids.stream().filter(id -> !existIds.contains(id)).toList();

            throw new ObjectException("以下标签不存在: " + notExistIds);
        }

        // 2. 删除关联表数据（使用in条件优化）
        producttagsMapper.delete(new LambdaQueryWrapper<ProductTags>().in(ProductTags::getTagId, ids));

        // 3. 批量删除主表数据
        baseMapper.deleteBatchIds(ids);
    }

    @Override
    public void exportTags(HttpServletResponse response) throws IOException {
        // 分页参数
        int pageSize = 2000;
        int pageNum = 1;

        setExcelResponseHeader(response, "标签数据");
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), TagExcelVO.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("标签列表").build();

            // 分页查询写入
            while (true) {
                Page<Tags> page = new Page<>(pageNum, pageSize);
                baseMapper.selectPage(page, null);

                List<TagExcelVO> chunk = page.getRecords().stream().map(this::convertToExcelVO)
                        .collect(Collectors.toList());

                excelWriter.write(chunk, writeSheet);

                if (page.getCurrent() >= page.getPages())
                    break;
                pageNum++;
            }
        }

    }

    private TagExcelVO convertToExcelVO(Tags tag) {
        return TagExcelVO.builder().name(tag.getName()).status(tag.getStatus()).build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importTags(@NotNull MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            throw new ObjectException("请选择上传文件");
        }

        // 一次性同步读取所有行
        List<TagExcelVO> vos = EasyExcel.read(file.getInputStream())
                .head(TagExcelVO.class)
                .sheet(1)
                .doReadSync();

        if (vos.isEmpty()) {
            throw new ObjectException("数据为空");
        }

        // 转成实体
        List<Tags> entities = vos.stream().map(vo -> {
            if (StrUtil.isBlank(vo.getName())) {
                throw new ObjectException("存在空标签名行");
            }
            return new Tags()
                    .setName(vo.getName().trim())
                    .setStatus(vo.getStatus() != null ? vo.getStatus() : 1);
        }).collect(Collectors.toList());

        // 分批插入，500 条一批
        int batchSize = 500;
        for (int i = 0; i < entities.size(); i += batchSize) {
            int toIndex = Math.min(i + batchSize, entities.size());
            List<Tags> chunk = entities.subList(i, toIndex);
            this.saveBatch(chunk);
        }
    }

    @Override
    public void downloadTemplate(@NotNull HttpServletResponse response) throws IOException {
        // 创建带示例的模板
        List<TagExcelVO> examples = Arrays.asList(new TagExcelVO("示例标签1", 1), new TagExcelVO("示例标签2", 0));
        setExcelResponseHeader(response, "标签导入模板");
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build()) {
            // 添加说明sheet
            WriteSheet instructionSheet = EasyExcel.writerSheet("填写说明").head(Collections.singletonList(List.of("注意事项")))
                    .build();
            excelWriter.write(Collections.singletonList(List.of("1. 状态列请填写'0'或'1'", "2. 标签名称不可重复")), instructionSheet);

            // 数据模板sheet
            WriteSheet dataSheet = EasyExcel.writerSheet("模板").head(TagExcelVO.class).build();
            excelWriter.write(examples, dataSheet);
        }

    }

    private TagVO toVO(Tags tag) {
        if (tag == null)
            return null;
        TagVO vo = new TagVO();
        BeanUtil.copyProperties(tag, vo);
        return vo;
    }

    private void setExcelResponseHeader(@NotNull HttpServletResponse response, String fileName) {
        String encodedName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20"); // 处理空格问题

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setHeader("Content-Disposition", "attachment; filename=" + encodedName + ".xlsx");
    }

    // 用户端新增方法实现
    @Override
    public List<TagVO> getAllEnabledTags() {
        LambdaQueryWrapper<Tags> qw = new LambdaQueryWrapper<Tags>()
                .eq(Tags::getStatus, 1)
                .orderByDesc(Tags::getUpdateAt);
        List<Tags> tags = baseMapper.selectList(qw);
        return tags.stream().map(this::toVO).collect(Collectors.toList());
    }

    @Override
    public List<TagVO> batchGetEnabledTags(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return List.of();
        }
        List<Integer> intIds = ids.stream().map(Long::intValue).collect(Collectors.toList());
        LambdaQueryWrapper<Tags> qw = new LambdaQueryWrapper<Tags>()
                .in(Tags::getId, intIds)
                .eq(Tags::getStatus, 1)
                .orderByDesc(Tags::getUpdateAt);
        List<Tags> tags = baseMapper.selectList(qw);
        return tags.stream().map(this::toVO).collect(Collectors.toList());
    }

    @Override
    public List<TagVO> searchEnabledTags(String keyword) {
        LambdaQueryWrapper<Tags> qw = new LambdaQueryWrapper<Tags>()
                .eq(Tags::getStatus, 1);

        if (StringUtils.hasText(keyword)) {
            qw.like(Tags::getName, keyword);
        }

        qw.orderByDesc(Tags::getUpdateAt);
        List<Tags> tags = baseMapper.selectList(qw);
        return tags.stream().map(this::toVO).collect(Collectors.toList());
    }

    @Override
    public List<TagVO> getPopularTags(Integer limit) {
        // 查询使用频率最高的标签
        List<Integer> popularTagIds = producttagsMapper.selectPopularTagIds(limit);

        if (popularTagIds.isEmpty()) {
            return List.of();
        }

        // 查询标签详情并转换为 VO，只返回启用的标签
        LambdaQueryWrapper<Tags> qw = new LambdaQueryWrapper<Tags>()
                .in(Tags::getId, popularTagIds)
                .eq(Tags::getStatus, 1)
                .orderByDesc(Tags::getUpdateAt);
        List<Tags> tags = baseMapper.selectList(qw);
        return tags.stream().map(this::toVO).collect(Collectors.toList());
    }
}
